﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.NetworkInformation;
using System.Threading;

namespace Protocolo.AnchoDeBanda
{
    public class AnchoDeBandaServidor
    {
        public double AnchoDeBandaTotal { get; set; }
        public double AnchoDeBandaXCliente { get; set; }
        public Dictionary<string, List<AnchoDeBandaCliente>> AnchoDeBandaClientes { get; set; } //La clave es la IP del cliente

        private Object updateLock = new Object();

        public double AnchoDeBandaDisponible
        { 
            get
            {
                return Math.Max(0, AnchoDeBandaTotal - AnchoDeBandaConsumido);
            } 
        }

        public double AnchoDeBandaConsumido
        { 
            get
            {
                var ret = (from cliente in AnchoDeBandaClientes.Keys
                            select ObtenerAnchoDeBandaConsumidoCliente(cliente)).Sum();

                EliminarDatosAnteriores();

                return ret;
            }
        }

        public AnchoDeBandaServidor()
        {
            this.AnchoDeBandaClientes = new Dictionary<string, List<AnchoDeBandaCliente>>();
        }

        public double ObtenerAnchoDeBandaConsumidoCliente(string IPCliente)
        {
            try
            {
                //Se obtiene el ancho de banda de los ultimos 2 segundos para tener una mayor precision
                lock (updateLock)
                {
                    if (AnchoDeBandaClientes.ContainsKey(IPCliente))
                    {
                        //Devuelvo la suma de lo consumido en el ultimo segundo
                        var ret = (from a in AnchoDeBandaClientes[IPCliente]
                                   where new TimeSpan(DateTime.Now.Ticks - a.TimeStamp).Seconds < 1
                                   select a.AnchoDeBandaConsumido).Sum();

                        return ret;
                    }
                    else
                        return 0;
                }
            }
            catch
            {
                return ObtenerAnchoDeBandaConsumidoCliente(IPCliente);
            }
        }

        public void ActualizarAnchoDeBandaCliente(string IPCliente, double KBytesConsumidos)
        {
            try
            {
                //Si lo que va a consumir el cliente (tanto para subir como para bajar archivo) hace que se pase
                //del ancho de banda total o por cliente hago que espere 1 segundo y pregunte nuevamente hasta que baje
                while (ObtenerAnchoDeBandaConsumidoCliente(IPCliente) + KBytesConsumidos > AnchoDeBandaXCliente ||
                        AnchoDeBandaConsumido + KBytesConsumidos > AnchoDeBandaTotal)
                    Thread.Sleep(1000);

                //Actualizo el ancho de banda agregando los KBytes consumidos a la lista
                var anchoDeBanda = new AnchoDeBandaCliente();
                anchoDeBanda.AnchoDeBandaConsumido = KBytesConsumidos;
                anchoDeBanda.TimeStamp = DateTime.Now.Ticks; //Uso ticks ya que incluye hasta milisegundos

                lock (updateLock)
                {
                    if (AnchoDeBandaClientes.ContainsKey(IPCliente))
                    {
                        AnchoDeBandaClientes[IPCliente].Add(anchoDeBanda);
                    }
                    else
                    {
                        AnchoDeBandaClientes.Add(IPCliente, new List<AnchoDeBandaCliente>() { anchoDeBanda });
                    }
                }
            }
            catch
            {
            }
        }

        public void EliminarDatosAnteriores()
        {
            try
            {
                lock (updateLock)
                {
                    //Quito de la lista los datos viejos (que se guardaron hace mas de x segundos)
                    foreach (var anchoDeBanda in AnchoDeBandaClientes.Values)
                    {
                        anchoDeBanda.RemoveAll(a => new TimeSpan(DateTime.Now.Ticks - a.TimeStamp).Seconds >= 1);
                    }

                    List<string> clientesAQuitar = new List<string>();
                    foreach (var cliente in AnchoDeBandaClientes.Keys)
                    {
                        if (AnchoDeBandaClientes[cliente].Count == 0)
                            clientesAQuitar.Add(cliente);
                    }

                    foreach (string cliente in clientesAQuitar)
                        AnchoDeBandaClientes.Remove(cliente);
                }
            }
            catch
            {
            }
        }
    }
}
